#pragma once
#include <folly/Conv.h>
#include <folly/CPortability.h>
#include <folly/Math.h>

#include <boost/multiprecision/cpp_dec_float.hpp>

#include <cstdint>

namespace tradev::util {

/*
精度说明:
本文中所有字段带有类型说明的相比如实际值有扩大倍数,详细倍数扩大规则如下(没有类型说明的就无扩大倍数):
类型:数量Qty        (如成交量,报买量等)的扩大倍数是 10^2
类型:数量CMDS-Qty   (如当日累计成交量等)的扩大倍数是 10^4
类型:数量RDI-Qty    (如增发量,行权量等)的扩大倍数是 10^10
类型:价格Price      (如参考价,报买价等)的扩大倍数是 10^6
类型:金额Amt        (如拆借金额等)的扩大倍数是 10^5
类型:比例Rate       (如最新利率,开盘利率等)的扩大倍数 10^6
类型:面额Par        (如发行价等)的扩大倍数是 10^10
类型:汇率ExRate     (如应计利息等)的扩大倍数 10^8
类型:利息Interest   (如每百元应计利息)的扩大倍数 10^15
*/

static constexpr auto kAmdVolumeMuFactor   = 100L;               // 如成交量,报买量等放大倍数
static constexpr auto kAmdCMDSQtyMuFactor  = 10000.0;            // 如当日累计成交量等放大倍数
static constexpr auto kAmdRDIQtyMuFactor   = 10000000000.0;      // 如增发量,行权量等放大倍数
static constexpr auto kAmdPriceMuFactor    = 1000000UL;          // 如参考价,报买价等等放大倍数
static constexpr auto kAmdAmountMuFactor   = 100000.0;           // 如拆借金额等等放大倍数
static constexpr auto kAmdRateMuFactor     = 1000000.0;          // 如最新利率,开盘利率等放大倍数
static constexpr auto kAmdParMuFactor      = 10000000000.0;      // 如发行价等放大倍数
static constexpr auto kAmdExRateMuFactor   = 100000000.0;        // 如应计利息等放大倍数
static constexpr auto kAmdInterestMuFactor = 1000000000000000.0; // 如每百元应计利息放大倍数

static constexpr double kDoubleMax = std::numeric_limits<double>::max();

/**
 * @brief 提取AMD快照中orig_time的日期部分
 * @param origTime 20230714091317000
 * @return eg.20210101
 */
FOLLY_ALWAYS_INLINE
int32_t convertOrigTimeToDate(int64_t origTime) {
  return folly::to<int32_t>(origTime / 1000000000L);
}

/**
 * @brief 提取AMD快照中orig_time的时间部分
 * @param origTime 20230714091317000
 * @return
 */
FOLLY_ALWAYS_INLINE
int32_t convertOrigTimeToTime(int64_t origTime) {
  auto date = convertOrigTimeToDate(origTime);
  return folly::to<int32_t>(origTime - date * 1000000000L);
}

FOLLY_ALWAYS_INLINE
std::vector<int> parseDateTime(folly::StringPiece dateTime) {
  if (dateTime.size() != 17) {
    return {0, 0, 0, 0, 0, 0, 0};
    //    throw std::invalid_argument("Input string must be in the format YYYYMMDDHHMMSSsss with exactly 17 characters.");
  }

  std::vector<int> result(7);

  try {
    result[0] = std::stoi(std::string(dateTime.subpiece(0, 4)));  // Year
    result[1] = std::stoi(std::string(dateTime.subpiece(4, 2)));  // Month
    result[2] = std::stoi(std::string(dateTime.subpiece(6, 2)));  // Day
    result[3] = std::stoi(std::string(dateTime.subpiece(8, 2)));  // Hour
    result[4] = std::stoi(std::string(dateTime.subpiece(10, 2))); // Minute
    result[5] = std::stoi(std::string(dateTime.subpiece(12, 2))); // Second
    result[6] = std::stoi(std::string(dateTime.subpiece(14, 3))); // Millisecond
  } catch (const std::exception& e) {
    throw std::invalid_argument("Error parsing dateTime string: " + std::string(e.what()));
  }

  return result;
}

FOLLY_ALWAYS_INLINE
double convertDouble(std::uint64_t ui64, const std::uint64_t factor) {
  using namespace boost::multiprecision;
  // 判断是否可以强转为double
  if (FOLLY_LIKELY(ui64 <= kDoubleMax)) {
    return static_cast<double>(ui64) / static_cast<double>(factor);
  }
  return static_cast<double>(cpp_dec_float_50(ui64) / factor);
  /*
  // 超出double的范围时
  // 计算整数部分
  auto integerPart = ui64 / factor;
  // 计算小数部分
  auto   remainder   = ui64 % factor;
  double decimalPart = static_cast<double>(remainder) / static_cast<double>(factor);

  // 合并整数部分和小数部分
  return static_cast<double>(integerPart) + decimalPart;
   */
}

/**
 * 转换价格(已优化缩放算法)
 * @tparam T uint64
 * @return 缩小后的价格
 */
FOLLY_ALWAYS_INLINE
double convertPrice(std::uint64_t ui64) {
  return convertDouble(ui64, kAmdPriceMuFactor);
}

FOLLY_ALWAYS_INLINE
int64_t convertVolume(std::uint64_t ui64) {
  return folly::to<int64_t>(ui64 / kAmdVolumeMuFactor);
}

/**
 * @brief 转换金额
 * @param i64 华锐行情中来的放大后的金额
 * @return 实际金额的double形式
 *
 * 注意这个方法专门设计为防溢出版本
 */
FOLLY_ALWAYS_INLINE
double convertAmount(std::uint64_t ui64) {
  return convertDouble(ui64, kAmdAmountMuFactor);
}

FOLLY_ALWAYS_INLINE
double convertRate(std::uint64_t ui64) {
  return convertDouble(ui64, kAmdRateMuFactor);
}

/*!
 * @brief 返回时间部分
 * @param datetimeInt YYYYMMDDHHMMSSsss
 * @return HHMMSSsss
 */
FOLLY_ALWAYS_INLINE
int64_t extractTime(int64_t datetimeInt) {
  // 通过模运算去掉前面的 YYYYMMDD 部分，只保留 HHMMSSsss
  int64_t timePart = datetimeInt % 1000000000;
  return timePart;
}

} // namespace tradev
